

#ifndef __M_HELP_H__
#define __M_HELP_H__

#include <iostream>
#include <fstream>
#include <sqlite3.h>
#include <vector>
#include <string>
#include <sstream>
#include <random>
#include <iomanip>
#include <atomic>
#include <sys/types.h>
#include <sys/stat.h>
#include "logger.hpp"

namespace xgmq
{
    class SqliteHelper
    {
    public:
        typedef int (*callback)(void *, int, char **, char **);
        SqliteHelper(const std::string &dbfile) : _dbfile(dbfile), _handler(nullptr)
        {
        }
        // 默认是按照串行化的方式打开
        bool open(int safe_leve = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, safe_leve | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nullptr);
            if (ret != SQLITE_OK)
            {
                // std::cout << "创建/打开数据库失败：";
                // std::cout << sqlite3_errmsg(_handler);
                ELOG("创建/打开数据库失败:%s", sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        // 执行语句
        bool exec(const std::string &sql, callback cb, void *arg)
        {
            int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                std::cout << sql << std::endl;
                // std::cout << "执行语句失败：";
                // std::cout << sqlite3_errmsg(_handler);
                ELOG("执行语句失败：%s\n%s", sql.c_str(), sqlite3_errmsg(_handler));
                return false;
            }
            return true;
        }
        void close()
        {
            if (_handler)
                sqlite3_close_v2(_handler);
        }

    private:
        sqlite3 *_handler;
        std::string _dbfile;
    };

    class StrSplit
    {
    public:
        static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
        {
            size_t idx = 0;
            size_t pos = 0;

            while (idx < str.size())
            {
                pos = str.find(sep, idx);
                if (pos == std::string::npos)
                {
                    result.push_back(str.substr(idx));
                    return result.size();
                }
                if (pos == idx)
                {
                    idx = pos + sep.size();
                    continue;
                }
                std::string tmp = str.substr(idx, pos - idx);
                result.push_back(tmp);
                idx = pos + sep.size();
            }
            return result.size();
        }
    };

    class UUIDHelper
    {
    public:
        static std::string uuid()
        {
            std::random_device rd;           // 生成机器随机数,作为伪随机数的种子
            std::mt19937_64 generator(rd()); // 通过梅塞旋转算法，生成一个伪随机数
            // std::cout<<generator()<<std::endl;

            // 将生成的随机数的区间控制到0-255之间
            std::uniform_int_distribution<int> distribution(0, 255);
            // std::cout<<distribution(generator)<<std::endl;

            std::stringstream ss;
            // 生成8个随机数，转化为16进制，再拼接再一起
            for (int i = 0; i < 8; i++)
            {
                // 设置位宽和填充
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
                if (i == 3 || i == 5 || i == 7)
                    ss << "-";
            }
            // std::cout << ss.str() << std::endl;

            // 再给生成的字符串后面添加序号
            static std::atomic<size_t> seq(1); // 定义一个原子类型整数，初始化为1
            size_t num = seq.fetch_add(1);

            // size_t 类型在64位下位8字节
            // 取出每字节的数据，转化位16进制，拼接在字符串的后面
            for (int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> i * 8) & (0xff));
                if (i == 6)
                    ss << "-";
            }
            return ss.str();
        }
    };

    class FileHelper
    {
    public:
        FileHelper(const std::string &filename) : _filename(filename) {}
        bool isExits()
        {
            struct stat st; // 可以获取文件的状态信息，遵循POSIX标准
            return stat(_filename.c_str(), &st) == 0;
        }
        // 获取文件大小
        size_t size()
        {
            struct stat st;
            stat(_filename.c_str(), &st);
            return st.st_size;
        }
        // 从文件指定位置读取len个数据
        bool read(char *buff, size_t offset, size_t len)
        {
            // 1，打开文件
            std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
            if (ifs.is_open() == false)
            {
                ELOG("打开文件%s 失败", _filename.c_str());
                return false;
            }
            // 2,移动到指定位置
            ifs.seekg(offset, std::ios::beg);
            if (ifs.good() == false)
            {
                ELOG("移动到%s文件的%ld位置失败", _filename.c_str(), offset);
                ifs.close();
                return false;
            }
            // 3,开始读文件
            ifs.read(buff, len);
            if (ifs.good() == false)
            {
                ELOG("读取%s文件失败", _filename.c_str());
                ifs.close();
                return false;
            }
            // 4,关闭文件
            ifs.close();
            return true;
        }
        // 读取文件全部内容
        bool read(std::string &buff)
        {
            buff.resize(size());
            return read(&buff[0], 0, size());
        }

        // 向文件指定位置写入指定长度的数据
        bool write(const char *data, size_t offset, size_t len)
        {
            std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
            if (fs.is_open() == false)
            {
                ELOG("打开%s文件失败", _filename.c_str());
                return false;
            }

            fs.seekg(offset, std::ios::beg);

            fs.write(data, len);
            if (fs.good() == false)
            {
                ELOG("读取%s文件失败", _filename.c_str());
                fs.close();
                return false;
            }
            fs.close();
            return true;
        }
        // 向文件开始处写入
        bool write(const std::string &data)
        {
            return write(data.c_str(), 0, data.size());
        }
        bool rename(const std::string &newname)
        {
            return ::rename(_filename.c_str(), newname.c_str()) == 0;
        }
        // 获取父路径
        //aaa/bbb/ccc/test.txt
        static std::string parentDirector(const std::string &path)
        {
            size_t pos = path.find_last_of("/");
            if (pos == std::string::npos)
            {
                return "./";
            }
            return path.substr(0, pos);
        }
        // 创建文件
        static bool createFile(const std::string &filename)
        {
            std::fstream fs(filename, std::ios::binary | std::ios::out);
            if (fs.is_open() == false)
            {
                ELOG("打开%s文件失败", filename.c_str());
                return false;
            }
            fs.close();
            return true;
        }
        // 移除文件
        static bool removeFile(const std::string &filename)
        {
            return ::remove(filename.c_str());
        }
        // 创建目录
        // 可能存在多级目录
        //aaa/bbb/ccc
        static bool createDirector(const std::string &path)
        {
            size_t pos = 0, idx = 0;
            while (idx < path.size())
            {
                pos = path.find("/",idx);
                if (pos == std::string::npos)
                {
                    return mkdir(path.c_str(), 0775) == 0;
                }
                // 注意这里每次截取的时候都是从0位置开始的，因为创建的是多级目录
                std::string p_path = path.substr(0, pos);
                int ret = mkdir(p_path.c_str(), 0775);
                if (ret != 0&&errno!=EEXIST)
                {
                    ELOG("创建%s目录失败", p_path.c_str());
                    return false;
                }
                idx = pos + 1;
            }
            return true;
        }
        //移除目录
        //同样也可能存在多级目录
        bool removeDirector(const std::string& path)
        {
            //调用库中的命令执行
            std::string cmd="rm -rf "+path;
            return ::system(cmd.c_str())!=-1;
        }

    private:
        std::string _filename;
    };
}

#endif
